/*
* File: CSocket.cpp
* Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
* Created on Aug 19, 2010, 10:14 PM
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*  1.Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*  2.Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  3.All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*       This product includes software developed by the University of
*       California, Berkeley and its contributors.
*  4.Neither the name of the University nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "CSocket-Inl.h"

namespace litevent{
namespace util{

CSocket::CSocket(char *szRemoteAddr, int dwPort)
{
    if (!szRemoteAddr)
        throw CException("Invalid parameters");

    this->Reset(dwPort);

    m_sockaddr.sin_addr.s_addr = inet_addr(szRemoteAddr);

    if (m_sockaddr.sin_addr.s_addr == INADDR_NONE)
    {
        struct hostent * pHost = gethostbyname(szRemoteAddr);
        if (!pHost)
            throw CException("Unable to solve this address");

        m_sockaddr.sin_addr.s_addr = **(int**) (pHost->h_addr_list);
    }

    if (connect(m_sock, (sockaddr*) & m_sockaddr, sizeof (sockaddr)) != 0)
        throw CException("connect() failed");
}

CSocket::CSocket(int dwPort, int dwBacklog)
{
    this->Reset(dwPort);

    if (bind(m_sock, (struct sockaddr *) &m_sockaddr, sizeof (m_sockaddr)) == -1)
        throw CException("bind() failed");

    if (listen(m_sock, dwBacklog) == -1)
        throw CException("listen() failed");
}

CSocket* CSocket::Accept(void)
{
    int nlen = sizeof (sockaddr);

    int socket = accept(m_sock, (sockaddr *) & m_sockaddr, (socklen_t *) & nlen);

    if (socket == -1)
        return (NULL);

    return ( new CSocket(socket,m_sockaddr));
}


bool CSocket::GetLocalPort(unsigned int& nPort) const
{
    struct sockaddr_in self;
    int len = sizeof (self);

    memset(&self, 0, len);

    if (getsockname(m_sock, (struct sockaddr*) &self, (socklen_t *) & len))
        return false;

    nPort = ntohs(self.sin_port);

    return true;
}

bool CSocket::GetRemotePort(unsigned int& nPort) const
{
    struct sockaddr_in self;
    int len = sizeof (self);

    memset(&self, 0, len);

    if (getpeername(m_sock, (struct sockaddr*) &self, (socklen_t *) & len))
        return false;

    nPort = ntohs(self.sin_port);

    return true;
}

bool CSocket::GetRemoteIP(char* szRemoteIP) const
{
    struct sockaddr_in self;
    unsigned int len = sizeof (self);

    memset(&self, 0, len);

    if (getpeername(m_sock, (struct sockaddr*) &self, (socklen_t *) & len))
        return false;

    // IPv4:
    //char ip4[INET_ADDRSTRLEN];  // space to hold the IPv4 string
    inet_ntop(AF_INET, &(self.sin_addr), szRemoteIP, INET_ADDRSTRLEN);
    //printf("The IPv4 address is: %s\n", ip4);

    return true;
}

bool CSocket::GetLocalIP(char* szLocalIP) const
{
    struct sockaddr_in addr;
    unsigned int addr_len = sizeof(addr);

    memset(&addr, 0, addr_len);

    if (getsockname(m_sock, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0)
        return false;

    inet_ntop(AF_INET, &(addr.sin_addr), szLocalIP, INET_ADDRSTRLEN);

    return true;
}

bool CSocket::MakeSocketNonBlock()
{
#ifdef WIN32
    unsigned long nonblocking = 1;
    ioctlsocket(this->m_sock, FIONBIO, (unsigned long*) &nonblocking);
#else
    int flags;

    if ((flags = fcntl(this->m_sock, F_GETFL, NULL)) < 0)
    {
        CLogger::sharedLogger()->WriteFormated("%s.%d: fcntl(%d,F_GETFL) failed ", __FILE__, __LINE__, this->m_sock);
	return false;
    }

    if (fcntl(this->m_sock, F_SETFL, flags | O_NONBLOCK) == -1)
    {
	CLogger::sharedLogger()->WriteFormated("%s.%d: fcntl(%d,F_SETFL) failed ", __FILE__, __LINE__, this->m_sock);
	return false;
    }
#endif
    return true;
}

}}